iT邦幫忙

2021 iThome 鐵人賽

DAY 1
0
Modern Web

Vite 出小蜜蜂~和卡比一起玩網頁遊戲開發!系列 第 8

[Day7] Vite 出小蜜蜂~Shoot 射擊系統!

  • 分享至 

  • xImage
  •  

Day7

Shoot

是時候幫我們的 LaserCannon 裝上子彈了!

Input

首先,當玩家按下 Space 時要發射 Laser
所以我們要在 InputSystem 新增 Space

export enum Key {
  Left,
  Right,
+ Space,
}
document.addEventListener("keydown", (event) => {
  if (event.code === "ArrowLeft") {
    pressed.add(Key.Left);
  }

  if (event.code === "ArrowRight") {
    pressed.add(Key.Right);
  }

+ if (event.code === "Space") {
+   pressed.add(Key.Space);
+ }
});

document.addEventListener("keyup", (event) => {
  if (event.code === "ArrowLeft") {
    pressed.delete(Key.Left);
  }

  if (event.code === "ArrowRight") {
    pressed.delete(Key.Right);
  }

+ if (event.code === "Space") {
+   pressed.delete(Key.Space);
+ }
});

Laser

新增 Laser
Laser 會根據 LaserCannon 的當前位置變換初始位置,
所以將初始位置定為傳入值,在執行時拋入。

Laser 會持續往前移動直到,所以每次畫面更新要增加 y

function Laser({ x, y }: Vector): GameObject & Transform & Renderer {
  return {
    renderer: {
      type: "graphics",
      src: [[1], [1], [1], [1]],
    },

    position: { x, y },

    update() {
      this.position.y -= 1;
    },
  };
}

Shooter

新增 Shooter 元件,以提供發射功能。

export interface Shooter {
  canShoot: boolean;
  shoot(): GameObject;
}

export function canShoot<T extends GameObject>(
  instance: T
): instance is T & Shooter {
  return "shoot" in instance;
}

GameObjectShooter玩家按下發射,我們要發射子彈。

透過 shoot 函式,在這邊不需要知道實際回傳的物件是什麼,
我們只需要將回傳新增到場景上即可。

一次只需要發射一發,所以執行一次後,記得要將 canShoot 切回 false

export default function Game(screen: Rectangle): Scene<Container> {
+ let instances: GameObject[] = [LaserCannon(screen), Squid()];

  return {
    update(delta) {
      instances.forEach((instance) => {
        if (canControl(instance)) {
          instance.handleInput(getKeyPressed());
        }

+       if (canShoot(instance) && instance.canShoot) {
+         requestAnimationFrame(() => {
+           instances = [...instances, instance.shoot()];
+         });
+         instance.canShoot = false;
+       }

        instance.update?.(delta);
      });
    },

    render(stage) {
      instances
        .filter(canRender)
        .forEach((instance) => render(stage, instance));
    },
  };
}

Shooting by Input

首先 Type 有點長,我們先重構一下,使其便於讀懂,並加入 Shooter 元件。

實作 Shooter 元件, Laser 的初始位置會根據 LaserCannon 的位置更動,
因為我們希望 Laser 是在 LaserCannon 中間的地方發射,所以要再調整一下數值。

handleInput 新增按下 Space 之後,將 canShoot 切成 true
但不希望射擊頻率的太快,所以我們增加了一點冷卻時間,
500ms 過後才能射擊一次。

+ type TLaserCannon = GameObject & Transform & Control & Renderer & Shooter;

export default function LaserCannon(screen: {
  width: number;
  height: number;
+ }): TLaserCannon {
+ let timePass = 0;

  return {
    renderer: {
      type: "graphics",
      src: [
        [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
        [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      ],
    },

    position: { x: 10, y: screen.height - 20 },

+   canShoot: false,
+   shoot() {
+     const { x, y } = this.position;
+     return Laser({ x: x + 5, y: y - 4 });
+   },

    handleInput(this: TLaserCannon, pressed) {
+     if (pressed.includes(Key.Space) && timePass > 500) {
+       this.canShoot = true;
+       timePass = 0;
+     }

      const width = screen.width - this.renderer.src[0].length;

      if (pressed.includes(Key.Left)) {
        this.position.x = clamp(0, width, this.position.x - 1);
        return;
      }

      if (pressed.includes(Key.Right)) {
        this.position.x = clamp(0, width, this.position.x + 1);
        return;
      }
    },

+   update(delta) {
+     timePass += delta;
+   },
  };
}

關於兔兔們:


上一篇
[Day6] Vite 出小蜜蜂~ Scene 場景!
下一篇
[Day8] Vite 出小蜜蜂~撞擊檢測 Collision Detection!
系列文
Vite 出小蜜蜂~和卡比一起玩網頁遊戲開發!19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言